{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 模型的使用代码\n",
    "\n",
    "模型训练好了之后要实际应用。对于模型部署有很多成熟的方案，如 Nvidia 的 TensorRT， Intel 的 OpenVINO 等，都可以做模型的高效部署，这里限于篇幅不涉及相关内容。\n",
    "\n",
    "在模型训练过程中，也可以使用使用框架提供的 API 做模型的简单部署以方便开发。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "import tensorflow as tf"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "首先要加载模型的标签用于展示，因为我们训练的时候就已经生成了标签文件，这里直接用写好的代码就可以。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "if os.path.exists(\"labels.txt\"):\n",
    "    with open(\"labels.txt\") as inf:\n",
    "        classes = [l.strip() for l in inf]\n",
    "else:\n",
    "    classes = os.listdir(\"worddata/train/\")\n",
    "    with open(\"labels.txt\", \"w\") as of:\n",
    "        of.write(\"\\r\\n\".join(classes))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "接着是模型的定义，这里直接将训练中使用的模型代码拿来即可。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "class MyModel(tf.keras.Model):\n",
    "    def __init__(self):\n",
    "        super(MyModel, self).__init__()\n",
    "        # 模型有两个主要部分，特征提取层和分类器\n",
    "\n",
    "        # 这里是特征提取层\n",
    "        self.feature = tf.keras.models.Sequential()\n",
    "        self.feature.add(self.conv(64))\n",
    "        self.feature.add(self.conv(64, add_pooling=True))\n",
    "\n",
    "        self.feature.add(self.conv(128))\n",
    "        self.feature.add(self.conv(128, add_pooling=True))\n",
    "\n",
    "        self.feature.add(self.conv(256))\n",
    "        self.feature.add(self.conv(256))\n",
    "        self.feature.add(self.conv(256, add_pooling=True))\n",
    "\n",
    "        self.feature.add(self.conv(512))\n",
    "        self.feature.add(self.conv(512))\n",
    "        self.feature.add(self.conv(512, add_pooling=True))\n",
    "\n",
    "        self.feature.add(self.conv(512))\n",
    "        self.feature.add(self.conv(512))\n",
    "        self.feature.add(self.conv(512, add_pooling=True))\n",
    "\n",
    "        self.feature.add(tf.keras.layers.GlobalAveragePooling2D())\n",
    "\n",
    "        self.feature.add(tf.keras.layers.Dense(4096, activation=\"relu\"))\n",
    "        self.feature.add(tf.keras.layers.BatchNormalization())\n",
    "        self.feature.add(tf.keras.layers.Dense(4096, activation=\"relu\"))\n",
    "        self.feature.add(tf.keras.layers.BatchNormalization())\n",
    "\n",
    "        self.feature.add(tf.keras.layers.Dropout(0.5))\n",
    "\n",
    "        # 这个简单的机构是分类器\n",
    "        self.pred = tf.keras.layers.Dense(100)\n",
    "\n",
    "    def conv(self, filters, add_pooling=False):\n",
    "        # 模型大量使用重复模块构建，\n",
    "        # 这里将重复模块提取出来，简化模型构建过程\n",
    "        model = tf.keras.models.Sequential(\n",
    "            [\n",
    "                tf.keras.layers.Conv2D(filters, 3, padding=\"same\", activation=\"relu\"),\n",
    "                tf.keras.layers.BatchNormalization(),\n",
    "            ]\n",
    "        )\n",
    "        if add_pooling:\n",
    "            model.add(\n",
    "                tf.keras.layers.MaxPool2D(\n",
    "                    pool_size=(2, 2), strides=None, padding=\"same\"\n",
    "                )\n",
    "            )\n",
    "        return model\n",
    "\n",
    "    def call(self, x):\n",
    "        # call 用来定义模型各个结构之间的运算关系\n",
    "\n",
    "        x = self.feature(x)\n",
    "        return self.pred(x)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "有了模型的定义之后，我们可以加载训练好的模型，跟模型训练的时候类似，我们可以直接加载模型训练中的 checkpoint。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "model lodaded\n"
     ]
    }
   ],
   "source": [
    "model = MyModel()\n",
    "ckpt = tf.train.get_checkpoint_state(\"./ckpts/\")\n",
    "if ckpt:\n",
    "    model.load_weights(ckpt.model_checkpoint_path)\n",
    "    print(\"model lodaded\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "对于数据，我们需要直接处理图片，因此这里导入一些图片处理的库和数据处理的库"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "from matplotlib import pyplot as plt\n",
    "from PIL import Image"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "直接打开某个图片"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<matplotlib.image.AxesImage at 0x7f521c3cfd30>"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "img = Image.open(\"worddata/validation/臣/1075875876fc1994ab864525089f675e6f9da575.jpg\")\n",
    "\n",
    "plt.imshow(img, cmap=\"gray\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "需要注意，模型在训练的时候，我们对数据进行了一些处理，在模型使用的时候，我们要对数据做一样的处理，如果不做的话，模型最终的结果会出现不可预料的问题。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(128, 128)"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "img = img.resize((128, 128))\n",
    "img = np.array(img) / 255\n",
    "img.shape"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "模型对图片数据的运算其实很简单，一行代码就可以。\n",
    "\n",
    "> 这里需要注意模型处理的数据是 4 维的，而上面的图片数据实际是 2 维的，因此要对数据进行维度的扩充。同时模型的输出是 2 维的，带 batch ，所以需要压缩一下维度。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "WARNING:tensorflow:Layer my_model is casting an input tensor from dtype float64 to the layer's dtype of float32, which is new behavior in TensorFlow 2.  The layer has dtype float32 because it's dtype defaults to floatx.\n",
      "\n",
      "If you intended to run this layer in float32, you can safely ignore this warning. If in doubt, this warning is likely only an issue if you are porting a TensorFlow 1.X model to TensorFlow 2.\n",
      "\n",
      "To change all layers to have dtype float64 by default, call `tf.keras.backend.set_floatx('float64')`. To change just this layer, pass dtype='float64' to the layer constructor. If you are the author of this layer, you can disable autocasting by passing autocast=False to the base Layer constructor.\n",
      "\n",
      "[8.4665515e-08, 5.306108e-07, 1.8298316e-06, 4.1379462e-05, 0.999956]\n",
      "['西', '定', '良', '白', '臣']\n"
     ]
    }
   ],
   "source": [
    "pred = np.squeeze(\n",
    "    tf.nn.softmax(model(img[np.newaxis, :, :, np.newaxis], training=False))\n",
    ")\n",
    "\n",
    "pred.argsort()[-5:]\n",
    "\n",
    "print([pred[idx] for idx in pred.argsort()[-5:]])\n",
    "print([classes[idx] for idx in pred.argsort()[-5:]])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这里只给出了 top5 的结果，可以看到，准确率还是不错的。"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
